
package org.sopt.dev.ImgKnight.frontier.Controller;

import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Timer;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.collections.set.SynchronizedSet;
import org.apache.log4j.Logger;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;

import org.sopt.dev.ImgKnight.frontier.Frontier;
import org.sopt.dev.ImgKnight.frontier.Executor.FillAgnetReadyChannel;
import org.sopt.dev.ImgKnight.frontier.NIO.NioTelnetStreamHandler;

public class TelnetController {
	
	private Frontier frontier = null;
	
	private AgentController agentController = null;
	
	private Channel listenChannel = null;
	
	private Set<Channel> channelSet = null;
	
	private Timer awaker = null;
	
	private ExecutorService NioBossExecutor = null;
	
	private ExecutorService NioWorkerExecutor = null;
	
	private Logger serviceLogger = null;
	
	private Logger exceptionLogger = null;
	
	@SuppressWarnings("unchecked")
	public TelnetController(AgentController agentController) {
		frontier = Frontier.getInstance();
		this.agentController = agentController;
		channelSet = SynchronizedSet.decorate(new HashSet<Channel>());
		serviceLogger = Logger.getLogger("frontier.service");
		exceptionLogger = Logger.getLogger("frontier.exception");
	}
	
	public void open() throws IllegalArgumentException {
		NioBossExecutor = Executors.newCachedThreadPool();
		NioWorkerExecutor = Executors.newCachedThreadPool();
		ChannelFactory factory 
			= new NioServerSocketChannelFactory(NioBossExecutor, NioWorkerExecutor);
		ServerBootstrap bootstrap = new ServerBootstrap(factory);	
		NioTelnetStreamHandler handler = new NioTelnetStreamHandler();
		bootstrap.getPipeline().addLast("handler", handler);
		bootstrap.setOption("child.tcpNoDelay", true);
		bootstrap.setOption("child.keepAlive", true);
		listenChannel = bootstrap.bind(new InetSocketAddress(frontier.getFrontierConfig().ControllerPort));
		awaker = new Timer();
		if( !listenChannel.isBound() )
			throw new IllegalArgumentException("ControllerPort port: "+frontier.getFrontierConfig().ControllerPort+" Bound Exception");
	}
	
	public void close() {
		awaker.cancel();
		channelSet.clear();
		if( NioBossExecutor != null )
			NioBossExecutor.shutdownNow();
		if( NioWorkerExecutor != null )
			NioWorkerExecutor.shutdownNow();
		if( listenChannel != null )
			listenChannel.close().awaitUninterruptibly();
	}
	
	public void onConnectionEstablished(Channel channel) {
		serviceLogger.info("RemoteController accepted from: "+channel.getRemoteAddress().toString());
		channelSet.add(channel);
	}
	
	public void onConnectionClosed(Channel channel) {
		serviceLogger.info("RemoteController disconnected from: "+channel.getRemoteAddress().toString());
		channel.close();
		channelSet.remove(channel);
	}
	
	public void onRecvedObject(Channel channel, Object obj) {
		String request = (String)obj;
		if( request.endsWith("\r\n") )
			request = request.substring(0, request.length()-2);
		int colon = request.indexOf(":");
		if( colon == -1 )
			return;
		String opcode = request.substring(0, colon);
		String oprand = request.substring(colon+1);
		if( opcode == null || oprand == null )
			return;	
		else if( opcode.equalsIgnoreCase("trk") ) {
			frontier.getsiteMapHashTable().reserveTracking(oprand);
			return;
		}
		else if( opcode.equalsIgnoreCase("info") ) {
			if( oprand.equalsIgnoreCase("signiture") ) {
				channel.write("Accepted by ImgKnight-Frontier On "+new Date()+"\r\n");
				channel.write("(C)Copyright 2009 Shout Our Passion Together(SOPT).\r\n\r\n");
				return;
			}
			else if( oprand.equalsIgnoreCase("agent") ) {
				Channel[] channels = frontier.getAgentController().getCurrentChannel();
				StringBuilder response = new StringBuilder();
				for(Channel c : channels)
					response.append(c.getRemoteAddress().toString());
				channel.write("info:agent="+response.toString()+"\r\n");
				return;
			}
			else if( oprand.equalsIgnoreCase("agent-trafic") ) {
				Long transferred = agentController.getTransferredMessages();
				channel.write("info:agent-trafic="+transferred.toString()+"\r\n");
				return;
			}
			else if( oprand.equalsIgnoreCase("condition") ) {
				channel.write("info:condition="+frontier.getCondition()+"\r\n");
				return;
			}
			else if( oprand.equalsIgnoreCase("highprio-url") ) {
				String[] highPrioUrl = frontier.getsiteMapHashTable().getHighPrioUrl();
				StringBuffer sb = new StringBuffer();
				if( highPrioUrl != null ) {
					for(String h : highPrioUrl)
						sb.append(h+" ");
				}
				channel.write("info:highprio-url="+sb.toString().trim()+"\r\n");
				return;
			}
			else if( oprand.equalsIgnoreCase("allow-pattern") ) {
				String[] allowUrlPatterns = frontier.getFrontierConfig().getAllowUrlPattern();
				StringBuffer sb = new StringBuffer();
				for(String p : allowUrlPatterns)
					sb.append(p+" ");
				channel.write("info:allow-pattern="+sb.toString().trim()+"\r\n");
				return;
			}
			else if( oprand.equalsIgnoreCase("deny-pattern") ) {
				String[] denyUrlPatterns = frontier.getFrontierConfig().getDenyUrlPattern();
				StringBuffer sb = new StringBuffer();
				for(String p : denyUrlPatterns)
					sb.append(p+" ");
				channel.write("info:deny-pattern="+sb.toString().trim()+"\r\n");
				return;
			}
		}
		else if( opcode.equalsIgnoreCase("sleep") ) {
			int sleepMiliSec = Integer.parseInt(oprand);
			agentController.closeAgentReadyChannelSet();
			awaker.schedule(new FillAgnetReadyChannel(agentController), sleepMiliSec);
			channel.write("sleep:"+sleepMiliSec+"=success\r\n");
			return;
		}
		else if( opcode.equalsIgnoreCase("add") ) {
			if( oprand.startsWith("highprio-url") ) {
				StringTokenizer tokenizer = new StringTokenizer(oprand.substring(13));
				List<String> hightPrio = new Vector<String>(tokenizer.countTokens());
				while( tokenizer.hasMoreTokens() )
					hightPrio.add(tokenizer.nextToken());
				int addCount = frontier.getsiteMapHashTable().offerToHighPrioMem(hightPrio);
				channel.write("add:highprio-url=success "+addCount+"\r\n");
				return;
			}
			else if( oprand.startsWith("allow-pattern") ) {
				StringTokenizer tokenizer = new StringTokenizer(oprand.substring(14));
				int addCount = tokenizer.countTokens();
				while( tokenizer.hasMoreTokens() )
					frontier.getFrontierConfig().addAllowUrlPatterns(tokenizer.nextToken());
				channel.write("add:allow-pattern=success "+addCount+"\r\n");
				return;
			}
			else if( oprand.startsWith("deny-pattern") ) {
				StringTokenizer tokenizer = new StringTokenizer(oprand.substring(13));
				int addCount = tokenizer.countTokens();
				while( tokenizer.hasMoreTokens() )
					frontier.getFrontierConfig().addDenyUrlPatterns(tokenizer.nextToken());
				channel.write("add:deny-pattern=success "+addCount+"\r\n");
				return;
			}
			else if( oprand.startsWith("exclusive") ) {
				/* TODO exclusive list*/
			}
		}
		else if( opcode.equalsIgnoreCase("rm") ) {
			if( oprand.startsWith("url") ) {
				StringTokenizer tokenizer = new StringTokenizer(oprand.substring(4));
				List<URL> removeUrlList = new Vector<URL>(tokenizer.countTokens());
				while( tokenizer.hasMoreTokens() ) {
					String token = tokenizer.nextToken();
					try { removeUrlList.add(new URL(token)); }
					catch(MalformedURLException ignore) {}
				}
				int rmCount = frontier.getsiteMapHashTable().removeUrl(removeUrlList, false, true, true);
				channel.write("rm:url=success "+rmCount+"\r\n");
				return;
			}
		}
		else if( opcode.equalsIgnoreCase("run") ) {
			if( oprand.equalsIgnoreCase("gc") ) {
				Runtime.getRuntime().gc();
				return;
			}
		}
		exceptionLogger.error("unknown request from: "+channel.getRemoteAddress().toString()+", "+request);
		channel.write("Unsupported operation: "+request+"\r\n");
	}
	
}
